package com.quan.server.endpoint;

import com.mark.util.CommonUtil;
import com.mark.util.JsonUtil;
import com.mark.util.wxmp.WeixinMPClient;
import com.quan.server.Config;
import com.quan.server.Const;
import com.quan.server.beans.Customer;
import com.quan.server.jpa.CustomerRepository;
import org.apache.commons.collections4.map.PassiveExpiringMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.util.Map;

@Component
@Path("/oauth")
public class OauthEndpoint {
    private static Logger logger = LoggerFactory.getLogger(OauthEndpoint.class);

    public static final String SCOPE_snsapi_userinfo = "snsapi_userinfo";
    public static final String SCOPE_snsapi_base = "snsapi_base";
    @Autowired
    private CustomerRepository customerRepository;

    @Autowired
    private Config config;

    private static final String PageLoginFinishWithWeChat = "PageLoginFinishWithWeChat";

    private PassiveExpiringMap<String, String> cache = new PassiveExpiringMap(30 * 1000); // Expiration time of 1 sec

    @Context
    private UriInfo uriInfo;

    @Value("${spring.profiles.active}")
    public String profileActive;

    @GET
    public Response login(@QueryParam("scope") @DefaultValue(SCOPE_snsapi_base) String scope,
                          @QueryParam("path") @DefaultValue(PageLoginFinishWithWeChat) String path) {
        logger.info("login, scope=" + scope);
        logger.info("login, path=" + path);
        //路径会带公众号上下文，要去掉 eg: /weixinmp/LoginFinishWithWeChat
        path = path.startsWith(Const.ContextWeixinmp) ? path.substring(Const.ContextWeixinmp.length()) : path;
        path = path.startsWith("/") ? path.substring(1, path.length()) : path;
        logger.error("login, handle path=" + path);
        if ("alpha".equals(profileActive)) {
            try {
                String customerId = customerRepository.findCustomerByWeixinId(Const.testWeixinId).id;
                String redirectURI = "http://" + uriInfo.getRequestUri().getHost() + ":3000" + Const.ContextWeixinmp + "/" + path + "/" + customerId;
                return Response.ok(redirectURI).build();
            } catch (Exception e) {
                logger.error("", e);
            }
        }
        try {
            String appId = config.qunWeixinMPAppId;
            String redirectURI = config.domainName + Const.ApplicationPath + "/oauth/callback/" + path;
            String oauthURL = String.format(
                "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=STATE#wechat_redirect",
                appId, redirectURI, scope);
            logger.error("oauthUrl: " + oauthURL);
            return Response.ok(oauthURL).build();
        } catch (Exception e) {
            logger.error("", e);
            return Response.ok().build();
        }
    }

    @GET
    @Path("callback/{path}")
    @Produces(MediaType.APPLICATION_JSON)
    synchronized public Response callback(@QueryParam("code") String code,
                                          @PathParam("path") @DefaultValue(PageLoginFinishWithWeChat) String path) {
        try {
            logger.error("callback, code=" + code);
            logger.error("callback, path=" + path);

            if (cache.containsKey(code)) {
                String redirectURI = cache.get(code);
                return Response.temporaryRedirect(new URI(redirectURI)).build();
            } else {
                String appId = config.qunWeixinMPAppId;
                String appSecret = config.qunWeixinMPAppSecret;
                Map result = WeixinMPClient.getAccessTokenByCode(appId, appSecret, code);
                if (result != null) {
                    String accessToken = result.containsKey(WeixinMPClient.KEY_ACCESS_TOKEN)
                        ? result.get(WeixinMPClient.KEY_ACCESS_TOKEN).toString()
                        : "";
                    String openId = result.containsKey(WeixinMPClient.KEY_OPENID) ? result.get(WeixinMPClient.KEY_OPENID).toString() : "";
                    String scope = result.containsKey(WeixinMPClient.KEY_ERROR_SCOPE)
                        ? result.get(WeixinMPClient.KEY_ERROR_SCOPE).toString()
                        : "";
                    String unionId = result.containsKey(WeixinMPClient.KEY_UNIONID) ? result.get(WeixinMPClient.KEY_UNIONID).toString()
                        : "";
                    Customer customer = customerRepository.findCustomerByWeixinMPOpenId(openId);
                    if (customer == null) {
                        customer = new Customer();
                        customer.id = CommonUtil.createRandomId();
                        customer.weixinMPOpenId = openId;
                        customer.weixinUnionId = unionId;
                        if (scope.equals(SCOPE_snsapi_userinfo)) {
                            Map<String, String> userInfo = WeixinMPClient.getUserInfo(accessToken, openId);
                            logger.error("getUserInfo:" + JsonUtil.bean2Json(userInfo));
                            Config.setProperty("userinfo", JsonUtil.bean2Json(userInfo));
                            String nickname = userInfo.get("nickname");
                            customer.weixinNickname = nickname;
                        }
                        customer = customerRepository.save(customer);
                    }
                    String customerId = customer.id;

                    String redirectURI = config.domainName + Const.ContextWeixinmp + "/" + path + "/" + customerId;
                    logger.error("redirect domainName:" + config.domainName);
                    logger.error("redirect context:" + Const.ContextWeixinmp);
                    logger.error("redirect path:" + path);

                    cache.put(code, redirectURI);
                    logger.error("redirect url:" + redirectURI);
                    return Response.temporaryRedirect(new URI(redirectURI)).build();
                } else {
                    String redirectURI = config.domainName + Const.ContextWeixinmp + "/" + path + "/error";
                    return Response.temporaryRedirect(new URI(redirectURI)).build();
                }
            }
        } catch (Exception e) {
            logger.error("", e);
            Response response = Response.ok().build();
            return response;
        }
    }
}